package com.lavender.extention.extention_interface;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;

import java.lang.reflect.Constructor;

/**
 * <p>
 *  SmartInstantiationAwareBeanPostProcessor
 * </p>
 *
 * @author: zhu.chen
 * @date: 2020/12/22
 * @version: v1.0.0
 */
//@Component
public class SmartInstantiationAwareBeanPostProcessorTest implements SmartInstantiationAwareBeanPostProcessor {

    /**
     * predictBeanType
     * 该触发点发生在postProcessBeforeInstantiation之前，
     * 这个方法用于预测Bean的类型，返回第一个预测成功的Class类型，如果不能预测返回null；
     * 当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。
     */
    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("SmartInstantiationAwareBeanPostProcessor predictBeanType");
        return beanClass;
    }

    /**
     * determineCandidateConstructors
     * 该触发点发生在postProcessBeforeInstantiation之后，用于确定该bean的构造函数之用，返回的是该bean的所有构造函数列表。
     * 用户可以扩展这个点，来自定义选择相应的构造器来实例化这个bean。
     */
    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("SmartInstantiationAwareBeanPostProcessor determineCandidateConstructors");
        return new Constructor[0];
    }


    /**
     * 该方法用于返回早期的Bean引用，即半成品的Bean，已经实例化但是还没有注入属性。
     * 循环依赖
     * 比如：CircularityA引用CircularityB，CircularityB引用CircularityC，CircularityC引用CircularityA
     * （1）Spring容器创建单例“circularityA” Bean：首先依据无參构造器创建“circularityA”Bean， 并暴露一个ObjectFactory，
     * 这个ObjectFactory用于返回提前暴露的circularityA，然后将“circularityA”放到“当前创建的Bean缓存池”中。
     * 然后进行setter注入“circularityB”；
     *
     * （2）Spring容器创建单例“circularityB” Bean：首先依据无參构造器创建“circularityB" Bean，并暴露一个ObjectFactory，
     * 于返回提前暴露的circularityB。然后将 circularityB 放入“当前创建的Bean缓存池”中，然后进行setter注入 circularityC ；
     *
     * （3）Spring容器创建单例“circularityC” Bean：首先依据无參构造器创建“circularityC”Bean，并暴露一个ObjectFactory，
     * 用于返回提前暴露的circularityC。并将 circularityC 放入“当前创建的Bean缓存池”中， 然后进行setter注入 circularityA ；
     * 进行注入“circularityA”时因为步骤（1）提前暴露了 circularityA 所以从之前的Cache里面拿BeanA，而不用反复创建。
     *
     * （4）最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean。 完毕setter注入。
     *
     * 该方法中，如果入参bean是 circularityA 这个Bean，则在第一次创建circularityA时会返回一个半成品的Bean，已经实例化但
     * 是还没有注入属性，我们称这个半成品的bean为exposedObject，即早期暴露的Bean。当circularityC创建时，会先注入这个半成品
     * beanA，这样就先完成了BeanC的创建，接着会完成BeanC的创建，到最后BeanA时，BeanC已经完成了创建，所以BeanA也就可以顺利完
     * 成。
     * 此外，对于“prototype”作用域Bean。Spring容器无法完毕依赖注入，由于“prototype”作用域的Bean，Spring容器不进行缓
     * 存，因此无法提前暴露一个创建中的Bean。
     * 还有就是，构造函数循环依赖注入时，也会抛异常。
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference");
        return bean;
    }

}
